/**
 * @Project TMHSC
 * @Author Tran Manh Hung
 * @Email: hungtmit@gmail.com | tmh@xwebgate.com
 * @Copyright © 2011 TMH. All rights reserved
 * @Createdate Jan 31, 2011
 * @Createtime 10:46:24 AM
 */

package com.tmh.study.aASA
{
	import flash.display.DisplayObject;
	import flash.display.Graphics;
	import flash.events.EventDispatcher;

	/**
	 * Encapsule grid for collision in class<br />
	 * For use in GridCollision2 & 3
	 */
	public class CollisionGrid extends EventDispatcher
	{
		private var _checks:Vector.<DisplayObject>;

		private var _grid:Vector.<Vector.<DisplayObject>>;

		private var _gridSize:Number;

		private var _width:Number;

		private var _height:Number;

		private var _numCells:uint;

		private var _numCols:uint;

		private var _numRows:uint;

		public function CollisionGrid(width:Number, height:Number, gridSize:Number)
		{
			_width = width;
			_height = height;
			_gridSize = gridSize;
			_numCols = Math.ceil(_width / _gridSize);
			_numRows = Math.ceil(_height / _gridSize);
			_numCells = _numCols * _numRows;
		}

		public function drawGrid(graphics:Graphics):void
		{
			// make line to represent grid
			graphics.lineStyle(0, 0x999999);

			for (var i:int = 0; i < _width; i += _gridSize)
			{
				graphics.moveTo(i, 0);
				graphics.lineTo(i, _height);
			}

			for (i = 0; i < _height; i += _gridSize)
			{
				graphics.moveTo(0, i);
				graphics.lineTo(_width, i);
			}
		}

		public function check(objects:Vector.<DisplayObject>):void
		{
			var numObject:int = objects.length;
			_grid = new Vector.<Vector.<DisplayObject>>(_numCells);
			_checks = new Vector.<DisplayObject>();

			// loop through all objects
			for (var i:int = 0; i < numObject; i++)
			{
				var obj:DisplayObject = objects[i];

				// use a single index to represent position in one dimensional grid
				var index:int = Math.floor(obj.y / _gridSize) * _numCols + Math.floor(obj.x / _gridSize);

				// only create cell here if an object is assigned to i
				if (_grid[index] == null)
				{
					_grid[index] = new Vector.<DisplayObject>;
				}

				// push object in a cell
				_grid[index].push(obj);
			}

			checkGrid();
		}

		private function checkGrid():void
		{
			// loop through each cell of grid
			for (var i:int = 0; i < _numCols; i++)
			{
				for (var j:int = 0; j < _numRows; j++)
				{
					// all the objects in the first cell against each other
					checkOneCell(i, j);
					checkTwoCell(i, j, i + 1, j); // right
					checkTwoCell(i, j, i + 1, j + 1); // lower right
					checkTwoCell(i, j, i, j + 1); // lower
					checkTwoCell(i, j, i - 1, j + 1); // lower left
				}
			}
		}

		private function checkOneCell(x:int, y:int):void
		{
			// get cell represent by x, y
			var cell:Vector.<DisplayObject> = _grid[y * _numCols + x];

			if (cell == null)
			{
				return;
			}

			var cellLength:int = cell.length;

			// compare all object to each other
			for (var i:int = 0; i < cellLength - 1; i++)
			{
				var cellA:DisplayObject = cell[i];

				for (var j:int = i + 1; j < cellLength; j++)
				{
					var cellB:DisplayObject = cell[j];
					_checks.push(cellA, cellB);
				}
			}
		}

		private function checkTwoCell(x1:int, y1:int, x2:int, y2:int):void
		{
			// make sure cell exists in grid
			if (x2 >= _numCols || x2 < 0 || y2 >= _numRows)
			{
				return;
			}

			// get each cell
			var cellA:Vector.<DisplayObject> = _grid[y1 * _numCols + x1];
			var cellB:Vector.<DisplayObject> = _grid[y2 * _numCols + x2];

			// make sure there are object in each
			if (cellA == null || cellB == null)
			{
				return;
			}

			var cellALength:int = cellA.length;
			var cellBLength:int = cellB.length;

			// compare all ojbects in one cell to all in other
			for (var i:int = 0; i < cellALength; i++)
			{
				var objA:DisplayObject = cellA[i];

				for (var j:int = 0; j < cellBLength; j++)
				{
					var objB:DisplayObject = cellB[j];
					_checks.push(objA, objB);
				}
			}
		}

		public function get checks():Vector.<DisplayObject>
		{
			return _checks;
		}
	}
}